/**
Company: Sarkar software technologys
WebSite: http://www.sarkar.cn
Author: yeganaaa
Date : 8/1/18
Time: 4:19 AM
 */

package com.ug_project.dict.engine.indextable

import java.util.*

class PointerBlock(indexTable: IndexTable, id: Int) : Block(indexTable, id), Iterable<Int> {
    companion object {
        const val maxSupportPointerSize = (4 * 4 * 4 * 4) + (4 * 4 * 4 * 4 * 4) + (4 * 4 * 4 * 4 * 4 * 4) + (4 * 4 * 4 * 4 * 4 * 4 * 4)
    }

    private val allDataBlockPointers = LinkedList<Int>()
    val allPointers: IntArray
        get() {
            iterator()
            return allDataBlockPointers.toIntArray()
        }

    var fourthLevelAddress: Int = 0
    var fifthLevelAddress: Int = 0
    var sixthLevelAddress: Int = 0
    var seventhLevelAddress: Int = 0

    init {
        buffer.apply {
            rewind()
            fourthLevelAddress = getInt()
            fifthLevelAddress = getInt()
            sixthLevelAddress = getInt()
            seventhLevelAddress = getInt()
        }
    }

    override fun writeToChannel() = autoWriteToChannel {
        putInt(fourthLevelAddress)
        putInt(fifthLevelAddress)
        putInt(sixthLevelAddress)
        putInt(seventhLevelAddress)
    }

    override fun iterator(): Iterator<Int> = with(this) {
        allDataBlockPointers.clear()
        fun recursive(addrBlock: AddressBlock) {
            if (addrBlock.blockId < 1) return

            if (addrBlock.level == 1) {
                allDataBlockPointers.addAll(addrBlock.AvailableAddrs)
                return
            }
            addrBlock.forEach(::recursive)
        }


        recursive(AddressBlock(this.indexTable, fourthLevelAddress, 4))
        recursive(AddressBlock(this.indexTable, fifthLevelAddress, 5))
        recursive(AddressBlock(this.indexTable, sixthLevelAddress, 6))
        recursive(AddressBlock(this.indexTable, seventhLevelAddress, 7))

        allDataBlockPointers.iterator()
    }

    fun addNewDataBlock(dataBlock: DataBlock) {
        var canBreak = false
        fun recursive(addressBlock: AddressBlock) {
            if (canBreak) return

            addressBlock.addrs.forEachIndexed { index, i ->
                if (canBreak) return@forEachIndexed
                if (i < 1) {
                    if (addressBlock.level > 1) {
                        addressBlock.addrs[index] = indexTable.allocateBlock()
                        recursive(AddressBlock(indexTable, addressBlock.addrs[index], addressBlock.level - 1))
                        addressBlock.writeToChannel()

                    } else {
                        addressBlock.addrs[index] = dataBlock.blockId
                        addressBlock.writeToChannel()
                        canBreak = true
                    }
                } else {
                    if (addressBlock.level > 1)
                        recursive(AddressBlock(indexTable, i, addressBlock.level - 1))
                }
            }
        }
        arrayOf(
                fourthLevelAddress,
                fifthLevelAddress,
                sixthLevelAddress,
                seventhLevelAddress
        ).forEachIndexed { index, i ->
            if (canBreak) return@forEachIndexed

            val block = if (i < 1) {
                when (index) {
                    0 -> AddressBlock(indexTable, indexTable.allocateBlock(), index + 4).also { fourthLevelAddress = it.blockId }
                    1 -> AddressBlock(indexTable, indexTable.allocateBlock(), index + 4).also { fifthLevelAddress = it.blockId }
                    2 -> AddressBlock(indexTable, indexTable.allocateBlock(), index + 4).also { sixthLevelAddress = it.blockId }
                    3 -> AddressBlock(indexTable, indexTable.allocateBlock(), index + 4).also { seventhLevelAddress = it.blockId }
                    else -> throw Exception("index must be 0 between 3")
                }
            } else AddressBlock(indexTable, i, index + 4)

            recursive(block)
        }
        writeToChannel()
        if (!canBreak) {
            throw Exception("Out of max index table size ")
        }
    }

    fun removeDataBlock(dataBlock: DataBlock) {
        indexTable.removeBlockAllocation(dataBlock.blockId)

        var canBreak = false
        fun recursive(addressBlock: AddressBlock) {
            if (canBreak) return

            addressBlock.addrs.forEachIndexed { index, i ->
                if (canBreak || i < 1) return@forEachIndexed
                if (addressBlock.level > 1) {
                    recursive(AddressBlock(indexTable, i, addressBlock.level - 1))
                } else {
                    if (i == dataBlock.blockId) {
                        addressBlock.addrs[index] = 0
                        addressBlock.writeToChannel()
                        canBreak = true
                    }
                }
            }
        }
        arrayOf(
                fourthLevelAddress,
                fifthLevelAddress,
                sixthLevelAddress,
                seventhLevelAddress
        ).filter { it > 0 }.forEachIndexed { index, i ->
            if (canBreak) return@forEachIndexed
            val block = AddressBlock(indexTable, i, index + 4)
            recursive(block)
        }
        writeToChannel()
        if (!canBreak) throw Exception("Cannot find data block :${dataBlock.blockId}")
    }
}


